package com.cll.learn.rpc.provider.handler;

import com.alibaba.fastjson.JSONObject;
import com.cll.learn.rpc.api.exception.service.IllegalParamException;
import com.cll.learn.rpc.api.exception.service.NotFoundServiceException;
import com.cll.learn.rpc.api.exception.service.ServiceException;
import com.cll.learn.rpc.common.dto.RpcRequest;
import com.cll.learn.rpc.common.dto.RpcResponse;
import com.cll.learn.rpc.common.enums.ResponseCodeEnum;
import com.cll.learn.rpc.provider.context.SpringApplicationContext;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import lombok.NonNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cglib.reflect.FastClass;
import org.springframework.cglib.reflect.FastMethod;

import java.lang.reflect.InvocationTargetException;
import java.util.UUID;

/**
 * 描述信息:
 * 服务端通道处理器
 * 接收消费者发送过来的请求
 * 反射，动态代理执行业务逻辑
 * 返回执行结果
 * @author CLL
 * @version 1.0
 * @date 2020/9/14 15:44
 */
public class RpcProviderHandler extends ChannelInboundHandlerAdapter {

    private static final Logger logger = LoggerFactory.getLogger(RpcProviderHandler.class);

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception, ServiceException {
        logger.info("===>>> channelRead");
        RpcResponse rpcResponse = null;
        if (msg instanceof RpcRequest) {
            RpcRequest rpcRequest = (RpcRequest) msg;
            try {
                Object handlerResult = handlerRequest(rpcRequest);
                rpcResponse = new RpcResponse(UUID.randomUUID().toString(),
                        rpcRequest.getRequestId(), ResponseCodeEnum.SUCCESS, handlerResult);
            } catch (Exception e) {
                logger.error("****** 服务端执行请求异常。" + e.getMessage(), e);
                if (e instanceof NotFoundServiceException) {
                    rpcResponse = new RpcResponse(UUID.randomUUID().toString(),
                            rpcRequest.getRequestId(), ResponseCodeEnum.NOT_FOUND, null);
                } else if (e instanceof IllegalParamException) {
                    rpcResponse = new RpcResponse(UUID.randomUUID().toString(),
                            rpcRequest.getRequestId(), ResponseCodeEnum.ILLEGAL_PARAM_TYPE, null);
                } else if (e instanceof ServiceException) {
                    rpcResponse = new RpcResponse(UUID.randomUUID().toString(),
                            rpcRequest.getRequestId(), ResponseCodeEnum.UNKNOWN_ERROR, null);
                } else if (e instanceof ClassNotFoundException) {
                    rpcResponse = new RpcResponse(UUID.randomUUID().toString(),
                            rpcRequest.getRequestId(), ResponseCodeEnum.NOT_FOUND, null);
                } else if (e instanceof InvocationTargetException) {
                    rpcResponse = new RpcResponse(UUID.randomUUID().toString(),
                            rpcRequest.getRequestId(), ResponseCodeEnum.INNER_ERROR, null);
                }  else {
                    rpcResponse = new RpcResponse(UUID.randomUUID().toString(),
                            rpcRequest.getRequestId(), ResponseCodeEnum.INNER_ERROR, null);
                }
            }
        } else {
            logger.error("****** 接收到参数类型不合法，服务端不能处理该请求");
            rpcResponse = new RpcResponse(UUID.randomUUID().toString(),
                    null, ResponseCodeEnum.ILLEGAL_PARAM_TYPE, null);
        }
        ctx.writeAndFlush(rpcResponse);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        logger.error("****** exceptionCaught 通道中读取数据连接异常");
        ctx.close();
    }

    private Object handlerRequest(RpcRequest rpcRequest) throws ClassNotFoundException, InvocationTargetException {
        @NonNull String className = rpcRequest.getClassName();
        Class<?> serviceInterface = SpringApplicationContext.getClass(className);
        Object serviceBean = SpringApplicationContext.getBean(serviceInterface);

        Class<?> serviceClass = serviceBean.getClass();
        FastClass fastClass = FastClass.create(serviceClass);

        @NonNull String methodName = rpcRequest.getMethodName();
        @NonNull Class<?>[] parameterTypes = rpcRequest.getParameterTypes();
        @NonNull Object[] parameters = rpcRequest.getParameters();

        if (parameterTypes.length != parameters.length) {
            logger.error("****** parameters length is not equals parameterTypes length...");
            throw new IllegalParamException("参数数组长度和参数类型数组长度不一致");
        } else {
            for (int i = 0; i < parameters.length; i++) {
                if (!parameterTypes[i].isInstance(parameters[i])) {
                    Class<?> parameterType = parameterTypes[i];
                    Object object = JSONObject.parseObject(JSONObject.toJSONString(parameters[i]), parameterType);
                    parameters[i] = object;
                }
            }
        }
        FastMethod method = fastClass.getMethod(methodName, parameterTypes);
        return method.invoke(serviceBean, parameters);
    }
}